/**
******************************************************************************************
* @file ProxyTab.c
* @brief Management of the message's emission by TCP/IP.
* @date 30 mars 2015
* @author Benjamin Lancinot
*****************************************************************************************/
/* COPYRIGHT ****************************************************************************
*****************************************************************************************
* Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************************/

#include "ProxyTab.h"

/*******************************************************************************************
* PROTOTYPES
********************************************************************************************/

void ProxyTab_start(ProxyTab *, int);

void ProxyTab_stop(ProxyTab *);

void ProxyTab_destroyUtilities(ProxyTab *);

void ProxyTab_destroy(ProxyTab **);

void ProxyTab_notifyBySpreader(ProxyTab*, int, char*);

void ProxyTab_notify(ProxyTab*, int, void*);

proxyTabMessage takeOneMessageFromBAL(int);

void putOneMessageOnBAL(int, BAL_msg *);

void ProxyTab_sendNewLuminosity(ProxyTab *, float);

void ProxyTab_sendNewHumidity(ProxyTab *, float);

void ProxyTab_sendNewTempCriticality(ProxyTab *, int);

void ProxyTab_sendNewHumCriticality(ProxyTab *, int);

void ProxyTab_sendNewTemperature(ProxyTab *, float);

void ProxyTab_sendNewBBState(ProxyTab *, float);

void ProxyTab_sendNewAverage(ProxyTab *, int, float);

void sendToTab(Sender *, proxyTabMessage);

void *ProxyTab_run(ProxyTab *);

/*******************************************************************************************
* FUNCTIONS
********************************************************************************************/

/**
 * @brief Constructor
 * @return Pointer to a new ProxyTab
 **/
ProxyTab* ProxyTab_create(){
	ProxyTab* This = (ProxyTab *) malloc(sizeof(*This));

	//Init function
	This->start = ProxyTab_start;
	This->stop = ProxyTab_stop;
	This->destroy = ProxyTab_destroy;
	This->notifyBySpreader = ProxyTab_notifyBySpreader;


	// create a message queue and analyze errors if there are.
	This->BAL_id = msgget(IPC_PRIVATE,0600|IPC_CREAT|IPC_EXCL);
	if (This->BAL_id < 0) {
		perror(strerror(errno));
		printf("failed to create message queue with msgqid = %d\n", This->BAL_id);
	}
	printf("message queue %d created\n",This->BAL_id);

	This->sender = Sender_create();
	//Init it as an observer
	This->observer = observerNew(This, (void (*)(void*, int, void*)) ProxyTab_notify);

	return This;
}


/**
 * @brief Run the thread which permit to send message by tcp/ip
 * @param This Pointer to the wanted ProxyTab
 * @param socketClient Value of the right socket client
 **/
void ProxyTab_start(ProxyTab *This, int socketClient){
	This->carryOn = TRUE;
	//Create class for send message
	This->sender->setSocket(This->sender, socketClient);

	//Creation of the thread which permitt to send from the Robot
	pthread_create(&This->proxyTabThread, NULL,(void *) &ProxyTab_run,(ProxyTab *)This);
}


/**
 * @brief Stop the emmision properly
 * @param This Pointer to the wanted ProxyTab
 **/
void ProxyTab_stop(ProxyTab *This){
	This->carryOn = FALSE;

	pthread_join(This->proxyTabThread,NULL);
}


/**
 * @brief Destroy all of the components of ProxyTab
 * @param This Pointer to the wanted ProxyTab
 **/
void ProxyTab_destroyUtilities(ProxyTab *This){
	int rc;
	//destruct associate object
        This->observer->destroy(This->observer);
         if (This->sender){
		This->sender->destroy(&This->sender);
        }
        //Close the message queue
	rc=msgctl(This->BAL_id,IPC_RMID,NULL);
	if (rc < 0) {
		perror( strerror(errno) );
		printf("msgctl (return queue) failed, rc=%d\n", rc);
	}

}


/**
 * @brief Destructor
 * @param This Double pointer to the wanted ProxyTab
 **/
void ProxyTab_destroy(ProxyTab **This){

        ProxyTab_destroyUtilities(*This);
        //destruct itself
	if (*This) {
		free(*This);
		*This = NULL;
	}
}

/**
 * @brief Called By Spreader, this method add a message in the message queue for warning clients about a changing
 * @param This Pointer to the wanted ProxyTab
 * @param action Id in the protocole for describe the changing
 * @param message Char sequence about the message
 **/
void ProxyTab_notifyBySpreader(ProxyTab* This, int action, char* message) {
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = atof(message);
	msgBAL->message.action = action;
	msgBAL->mtype = 1;

	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}

/**
 * @brief Observer Method called upon the receiption of an incoming event.This method react with thE type of information which have received
 * @param This Pointer to the wanted ProxyTab
 * @param numero Value of the information type of the data received
 * @param subject Pointer to the subject to use
 **/
void ProxyTab_notify(ProxyTab* This, int numero, void* subject) {

        if (numero == 0){
		Electrician* temp = (Electrician*) subject;
		ProxyTab_sendNewLuminosity(This, temp->luminosity);
        }
        if (numero == 1){
		MercureLover* temp = (MercureLover*) subject;
		ProxyTab_sendNewTemperature(This, temp->temperature);
        }
	if (numero == 2){
		HumidityTester * temp = (HumidityTester*) subject;
		ProxyTab_sendNewHumidity(This, temp->humidity);
        }
	if (numero == 3){
		SoundEngineer * temp = (SoundEngineer*) subject;
		ProxyTab_sendNewBBState(This, temp->BBState);
        }
	if (numero == 4){
		MercureController * temp = (MercureController*) subject;
		ProxyTab_sendNewTempCriticality(This, temp->criticality);
	}
	if (numero == 5){
		HumidityController * temp = (HumidityController*) subject;
		ProxyTab_sendNewHumCriticality(This, temp->criticality);
	}
	if (numero == 7){
	 	AverageMaker * temp = (AverageMaker*) subject;
		ProxyTab_sendNewAverage(This, temp->daddy, temp->averageNumerator / temp->totalTime);
	}

}


/**
 * @brief Recover a message to send by tcp/ip on the message queue
 * @param BAL_id Value of the message queue id
 * @return The message to send by TCP/IP
 **/
proxyTabMessage takeOneMessageFromBAL(int BAL_id){
	int rc;
	BAL_msg new_msgBAL;
        new_msgBAL.mtype =1;
        new_msgBAL.message.carryOn = FALSE;

	// read the message from queue
	rc = msgrcv(BAL_id, &new_msgBAL, sizeof(new_msgBAL.message), 0, IPC_NOWAIT);
	if (rc > 0) {
		new_msgBAL.message.carryOn = TRUE;
	}

	return new_msgBAL.message;
}


/**
 * @brief Add a message to send by TCP/IP on the message queue
 * @param BAL_id Value of the message queue id
 * @param msgMQ Pointer to a Message to add
 **/
void putOneMessageOnBAL(int BAL_id, BAL_msg* msgBAL) {

	int rc;

	// send the message to queue
	rc = msgsnd(BAL_id,msgBAL, sizeof(msgBAL->message), 0);
	if (rc < 0) {
		perror( strerror(errno) );
		printf("msgsnd failed, rc = %d\n", rc);
	}

}


/**
 * @brief Ask to put new luminosity on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newLuminosity Value of a new luminosity to send
 **/
void ProxyTab_sendNewLuminosity(ProxyTab *This, float newLuminosity){
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = newLuminosity;
	msgBAL->message.action = SEND_LIGHT;
	msgBAL->mtype = 1;

	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Ask to put new humidity on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newHumidity Value of a new humidity to send
 **/
void ProxyTab_sendNewHumidity(ProxyTab *This, float newHumidity){
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = newHumidity;
	msgBAL->message.action = SEND_HUMIDITY;
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Ask to put new temperature criticality on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newCriticality Value of a new criticality to send
 **/
void ProxyTab_sendNewTempCriticality(ProxyTab *This, int newCriticality){
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = (float) newCriticality;
	msgBAL->message.action = SEND_CRITICALITY_TEMPERATURE;
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Ask to put new humidity criticality on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newCriticality Value of a new criticality to send
 **/
void ProxyTab_sendNewHumCriticality(ProxyTab *This, int newCriticality){
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = (float) newCriticality;
	msgBAL->message.action = SEND_CRITICALITY_HUMIDITY;
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Ask to put new temperature on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newTemperature Value of a new temperature to send
 **/
void ProxyTab_sendNewTemperature(ProxyTab *This, float newTemperature){
	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = newTemperature;
	msgBAL->message.action = SEND_TEMPERATURE;
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}

/**
 * @brief Ask to put new BBState on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param newBBState Value of a new BBState to send
 **/
void ProxyTab_sendNewBBState(ProxyTab *This, float newBBState){
    	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = newBBState;
	msgBAL->message.action = SEND_BBSTATE;
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Ask to put new luminosity on the message queue
 * @param This Pointer to the wanted ProxyTab
 * @param daddyOfAverage Type of average (Humidity or Temperature)
 * @param average Value of a new average to send
 **/
void ProxyTab_sendNewAverage(ProxyTab *This, int daddyOfAverage, float average){
    	BAL_msg * msgBAL= (BAL_msg *) calloc(1,sizeof(BAL_msg));
	msgBAL->message.valeur = average;
        if (daddyOfAverage ==0){
		msgBAL->message.action = SEND_AVERAGE_TEMPERATURE;
        }else{
		msgBAL->message.action = SEND_AVERAGE_HUMIDITY;
	}
	msgBAL->mtype = 1;


	putOneMessageOnBAL(This->BAL_id, msgBAL);
        free(msgBAL);
}


/**
 * @brief Send message by tcp/ip thanks to Sender
 * @param sender Pointer to the right sender for send by TCP/IP
 * @param message Structure of the message to send
 **/
void sendToTab(Sender *sender, proxyTabMessage message){

	sender->sendMessage(sender, message.action, message.valeur);
	message.action = NTD;
}


/**
 * @brief Function use by proxyTab_Thread for sending message by TCP/IP
 * @param This Pointer to the wanted ProxyTab
 * @return The final value for the thread
 **/
void *ProxyTab_run(ProxyTab *This){
	proxyTabMessage message;

	while(This->carryOn){

		message = takeOneMessageFromBAL(This->BAL_id);
		if (message.carryOn == TRUE){
			sendToTab(This->sender,message);
		}
		usleep(10000);
	}
        printf("Proxy Fin run\n");
        return 0;
}
